﻿using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Printing;
using System.ComponentModel;

using System.Runtime.InteropServices;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using RTFViewerControl;
using System.Text;
 

namespace ILookintoControl
{
    public class RichText50W : RichTextBox
    {
        ToolTip tip;
        
        public RichText50W()
        {
            tip = new ToolTip();
            //tip.IsBalloon = true;

            
           
            ContextMenu cm = new ContextMenu();

            MenuItem mi = new MenuItem("Insert bookmark");
            mi.Click += new EventHandler(insertBookmarks);
            cm.MenuItems.Add(mi);

            mi = new MenuItem("Delete bookmark");
            mi.Click += new EventHandler(deleteBookmark);
            cm.MenuItems.Add(mi);

            mi = new MenuItem("Add Comment");
            mi.Click += new EventHandler(addComment);
            cm.MenuItems.Add(mi);

            mi = new MenuItem("Cut");
            mi.Click += new EventHandler(mi_Cut);
            cm.MenuItems.Add(mi);

            mi = new MenuItem("Copy");
            mi.Click += new EventHandler(mi_Copy);
            cm.MenuItems.Add(mi);

            mi = new MenuItem("Paste");
            mi.Click += new EventHandler(mi_Paste);
            cm.MenuItems.Add(mi);

            mi = new MenuItem("About");
            mi.Click += new EventHandler(mi_About);
            cm.MenuItems.Add(mi);
            this.ContextMenu = cm;

            this.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(Link_Clicked);
            //this.OnMouseClick + new 


        }
        
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            int[] a = { /*0x204e, 0x281, 0x7, 0x2111 ,*/ 0x30 };
            for (int i = 0; i < a.Length; i++)
            {
                if (m.Msg == a[i]) return;
                //this.Rtf = damnit;
                //return;
            }
            //tw.WriteLine(String.Format("{0:x}",  m.Msg));
            //tw.Flush();
            base.WndProc(ref m);

        }

        public RTFViewerControl.ActiveXUserControl axControl;


       /* private static IntPtr moduleHandle;

        protected override CreateParams CreateParams
        {
            get
            {
                if (moduleHandle == IntPtr.Zero)
                {
                    moduleHandle = LoadLibrary("msftedit.dll");
                    if ((long)moduleHandle < 0x20)
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not load Msftedit.dll");
                    }
                }
                CreateParams createParams = base.CreateParams;
                createParams.ClassName = "RichEdit50W";
                if (this.Multiline)
                {
                    if (((this.ScrollBars & RichTextBoxScrollBars.Horizontal) != RichTextBoxScrollBars.None) &&
                              !base.WordWrap)
                    {
                        createParams.Style |= 0x100000;
                        if ((this.ScrollBars & ((RichTextBoxScrollBars)0x10)) != RichTextBoxScrollBars.None)
                        {
                            createParams.Style |= 0x2000;
                        }
                    }
                    if ((this.ScrollBars & RichTextBoxScrollBars.Vertical) != RichTextBoxScrollBars.None)
                    {
                        createParams.Style |= 0x200000;
                        if ((this.ScrollBars & ((RichTextBoxScrollBars)0x10)) != RichTextBoxScrollBars.None)
                        {
                            createParams.Style |= 0x2000;
                        }
                    }
                }
                if ((BorderStyle.FixedSingle == base.BorderStyle) && ((createParams.Style & 0x800000) != 0))
                {
                    createParams.Style &= -8388609;
                    createParams.ExStyle |= 0x200;
                }
                return createParams;
            }
        }

        // P/Invoke declarations
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr LoadLibrary(string path);
        */
        //START : make this control trasparent
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr LoadLibrary(string lpFileName);
         protected override CreateParams CreateParams
        {
            get
            {
                CreateParams prams = base.CreateParams;
               /* if (LoadLibrary(@"C:\Program Files\Common Files\microsoft shared\OFFICE14\RICHED20.DLL") != IntPtr.Zero
                || LoadLibrary(@"C:\Program Files\Common Files\microsoft shared\OFFICE13\RICHED20.DLL") != IntPtr.Zero
                || LoadLibrary(@"C:\Program Files\Common Files\microsoft shared\OFFICE12\RICHED20.DLL") != IntPtr.Zero)
                {
                    //prams.ExStyle |= 0x020; // transparent
                    prams.ClassName = "RICHEDIT60W";
                }
                else*/ if (LoadLibrary("msftedit.dll") != IntPtr.Zero)
                {
                    prams.ClassName = "RICHEDIT50W";
                }
                return prams;
            }
        }
        // END

        protected void Link_Clicked(object sender, System.Windows.Forms.LinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(e.LinkText);
        }

       

        /*protected override void OnClick(EventArgs e) {
            MessageBox.Show("tx");
        } */
        //Convert the unit used by the .NET framework (1/100 inch) 
        //and the unit used by Win32 API calls (twips 1/1440 inch)
        private const double anInch = 14.4;

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct CHARRANGE
        {
            public int cpMin;         //First character of range (0 for start of doc)
            public int cpMax;           //Last character of range (-1 for end of doc)
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct FORMATRANGE
        {
            public IntPtr hdc;             //Actual DC to draw on
            public IntPtr hdcTarget;       //Target DC for determining text formatting
            public RECT rc;                //Region of the DC to draw to (in twips)
            public RECT rcPage;            //Region of the whole DC (page size) (in twips)
            public CHARRANGE chrg;         //Range of text to draw (see earlier declaration)
        }

        private const int WM_USER = 0x0400;
        private const int EM_FORMATRANGE = WM_USER + 57;

        [DllImport("USER32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);


        // Render the contents of the RichTextBox for printing
        //	Return the last character printed + 1 (printing start from this point for next page)
        public int Print(int charFrom, int charTo, PrintPageEventArgs e)
        {
            //Calculate the area to render and print
            RECT rectToPrint;
            rectToPrint.Top = (int)(e.MarginBounds.Top * anInch);
            rectToPrint.Bottom = (int)(e.MarginBounds.Bottom * anInch);
            rectToPrint.Left = (int)(e.MarginBounds.Left * anInch);
            rectToPrint.Right = (int)(e.MarginBounds.Right * anInch);

            //Calculate the size of the page
            RECT rectPage;
            rectPage.Top = (int)(e.PageBounds.Top * anInch);
            rectPage.Bottom = (int)(e.PageBounds.Bottom * anInch);
            rectPage.Left = (int)(e.PageBounds.Left * anInch);
            rectPage.Right = (int)(e.PageBounds.Right * anInch);

            IntPtr hdc = e.Graphics.GetHdc();

            FORMATRANGE fmtRange;
            fmtRange.chrg.cpMax = charTo;				//Indicate character from to character to 
            fmtRange.chrg.cpMin = charFrom;
            fmtRange.hdc = hdc;                    //Use the same DC for measuring and rendering
            fmtRange.hdcTarget = hdc;              //Point at printer hDC
            fmtRange.rc = rectToPrint;             //Indicate the area on page to print
            fmtRange.rcPage = rectPage;            //Indicate size of page

            IntPtr res = IntPtr.Zero;

            IntPtr wparam = IntPtr.Zero;
            wparam = new IntPtr(1);

            //Get the pointer to the FORMATRANGE structure in memory
            IntPtr lparam = IntPtr.Zero;
            lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
            Marshal.StructureToPtr(fmtRange, lparam, false);

            //Send the rendered data for printing 
            res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam);

            //Free the block of memory allocated
            Marshal.FreeCoTaskMem(lparam);

            //Release the device context handle obtained by a previous call
            e.Graphics.ReleaseHdc(hdc);

            //Return last + 1 character printer
            return res.ToInt32();
        }

        public int Draw(int charFrom, int charTo, Graphics g, int x, int y, int x1, int y1, double conversion, double conversionY)
        {

            //Calculate the area to render and print
            RECT rectToPrint;
            rectToPrint.Top = (int)(y * conversionY);
            rectToPrint.Bottom = (int)(y1 * conversionY);
            rectToPrint.Left = (int)(x * conversion);
            rectToPrint.Right = (int)(decimal)(x1 * conversion);

            //Calculate the size of the page
            RECT rectPage;
            rectPage.Top = (int)(y * conversionY);
            rectPage.Bottom = (int)(y1 * conversionY);
            rectPage.Left = (int)(x * conversion);
            rectPage.Right = (int)(x1 * conversion);

            IntPtr hdc = g.GetHdc();

            FORMATRANGE fmtRange;
            fmtRange.chrg.cpMax = charTo;				//Indicate character from to character to 
            fmtRange.chrg.cpMin = charFrom;
            fmtRange.hdc = hdc;                    //Use the same DC for measuring and rendering
            fmtRange.hdcTarget = hdc;              //Point at printer hDC
            fmtRange.rc = rectToPrint;             //Indicate the area on page to print
            fmtRange.rcPage = rectPage;            //Indicate size of page


            IntPtr res = IntPtr.Zero;

            IntPtr wparam = IntPtr.Zero;
            wparam = new IntPtr(1);

            //Get the pointer to the FORMATRANGE structure in memory
            IntPtr lparam = IntPtr.Zero;
            lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
            Marshal.StructureToPtr(fmtRange, lparam, false);

            //Send the rendered data for printing 
            res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam);

            //Free the block of memory allocated
            Marshal.FreeCoTaskMem(lparam);

            //Release the device context handle obtained by a previous call
            g.ReleaseHdc(hdc);

            //Return last + 1 character printer
            return res.ToInt32();
        }
        Point loc = new Point(0, 0);
        protected override void OnMouseDown(MouseEventArgs e)
        {
            MouseEventArgs me = (MouseEventArgs)e;
            loc = me.Location;
            //MessageBox.Show(me.Location.ToString());
        }

        bool show = false;
       
       
        protected override void OnMouseMove(MouseEventArgs e)
        {
         
            MouseEventArgs me = (MouseEventArgs)e;
             
            int p = this.GetCharIndexFromPosition(me.Location);
           
            bool toshow = false;
            foreach(KeyValuePair<String, String[]> kv in commentDict)
            {
                if (Int32.Parse(kv.Value[0]) <= p && p<= Int32.Parse(kv.Value[0]) + Int32.Parse(kv.Value[1]))
                {
                    toshow = true;
                    if (show) break;

                    Point pp = this.Location;
                    Point ee = this.GetPositionFromCharIndex(Int32.Parse(kv.Value[0]) + Int32.Parse(kv.Value[1]));
                   
                    tip.Show( Wrap(kv.Value[2], 30), this, pp.X + ee.X-10, pp.Y + ee.Y- 35);
                    

                    show = true;
                    toshow = true;
                    
                    return;
                }
            }
            if (!toshow)
            {

                show = false;
                tip.Hide(this);
                
            }
            //if (!show) popup.
        }

       

        Dictionary<String, String[]> bookMarkDict = new Dictionary<String, String[]>();
        Dictionary<String, String[]> commentDict = new Dictionary<String, String[]>();
        InputBoxValidation validation = delegate(string val)
        {
            if (val == "")
                return "Value cannot be empty.";
            if (!(new Regex(@"\w+")).IsMatch(val))
                return "Not a valid mark";
            return "";
        };


        void addComment(object sender, EventArgs e)
        {
            String value = this.SelectedText;
            String text = value;
            String start = this.SelectionStart + "";
            String length = this.SelectionLength + "";
            if (InputBox.Show("Enter your comment", "Comment:", ref value, validation) == DialogResult.OK)
            {

                commentDict[text] = new String[] { start, length, value };
                this.SelectionBackColor = Color.LightGreen;
                this.SelectedText = text;
                persitComment();
            }
        }


        void insertBookmarks(object sender, EventArgs e)
        {

            String value = this.SelectedText;
            String text = value;
            String start = this.SelectionStart + "";
            String length = this.SelectionLength + "";
            if (this.axControl.bookMarkCombo.Items.Contains(value))
                value += 1;
            if (InputBox.Show("Enter your bookmark name", "Bookmark:", ref value, validation) == DialogResult.OK)
            {

                bookMarkDict[value] = new String[] { start, length, text };
                this.SelectionBackColor = Color.Chocolate;
                this.SelectedText = text;
                this.axControl.bookMarkCombo.Items.Add(value);
                persitBookMark();
            }


        }
        public string HashString(string Value)
        {
            System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] data = System.Text.Encoding.ASCII.GetBytes(Value);
            data = x.ComputeHash(data);
            string ret = "";
            for (int i = 0; i < data.Length; i++)
                ret += data[i].ToString("x2").ToLower();
            return ret;
        }

        public void readBookMark()
        {
            try
            {
                String persistence = this.HashString(this.Text);
                // create reader & open file
                TextReader tr = new StreamReader(persistence);

                // read a line of text
                String line;
                while ((line = tr.ReadLine()) != null)
                {
                    String[] fields = line.Split(new char[] { ',' });
                    this.axControl.bookMarkCombo.Items.Add(fields[0]);
                    bookMarkDict[fields[0]] = new String[] { fields[1], fields[2], fields[3] };
                    int start = Int32.Parse(fields[1]);
                    int length = Int32.Parse(fields[2]);
                    this.Select(start, length);
                    this.SelectionBackColor = Color.Chocolate;
                    this.SelectedText = fields[3];
                }

                // close the stream
                tr.Close();
            }
            catch (Exception e) { }
        }
        private void persitBookMark()
        {
            try
            {
                String persistence = HashString(this.Text);

                TextWriter tw = new StreamWriter(persistence);

                foreach (KeyValuePair<String, String[]> pair in bookMarkDict)
                    tw.WriteLine(pair.Key + "," + pair.Value[0] + "," + pair.Value[1] + "," + pair.Value[2]);

                // close the stream
                tw.Close();
            }
            catch (Exception e) { }

        }

        public void readComment()
        {
         try
            {
                String persistence = this.HashString(this.Text);
                // create reader & open file
                TextReader tr = new StreamReader(persistence+".cm");

                // read a line of text
                String line;
                while ((line = tr.ReadLine()) != null)
                {
                    String[] fields = line.Split(new char[] { '|' });
                    commentDict[fields[0]] = new String[] { fields[1], fields[2], fields[3] };
                    int start = Int32.Parse(fields[1]);
                    int length = Int32.Parse(fields[2]);
                    this.Select(start, length);
                    this.SelectionBackColor = Color.LightGreen;
                    this.SelectedText = fields[0];
                }

                // close the stream
                tr.Close();
            }
            catch (Exception e) { }
        }

        private void persitComment()
        {
            try
            {
                String persistence = HashString(this.Text);

                TextWriter tw = new StreamWriter(persistence+".cm");

                foreach (KeyValuePair<String, String[]> pair in commentDict)
                    tw.WriteLine(pair.Key + "|" + pair.Value[0] + "|" + pair.Value[1] + "|" + pair.Value[2]);

                // close the stream
                tw.Close();
            }
            catch (Exception e) { }

        }

        String currentBookMark = "";

        void deleteBookmark(object sender, EventArgs e)
        {

            //this.GetCharFromPosition();
            currentBookMark = "";
            int i = this.GetCharIndexFromPosition(loc);
            //MessageBox.Show(i + loc.ToString() + this.GetCharIndexFromPosition(loc).ToString() + this.GetCharFromPosition(loc));
            int start = -1;
            int length = -1;
            String oriText = "";
            foreach (KeyValuePair<String, String[]> pair in bookMarkDict)
            {
                start = Int32.Parse(pair.Value[0]);
                length = Int32.Parse(pair.Value[1]);
                if (i >= start && i <= start + length)
                {

                    oriText = pair.Value[2];
                    currentBookMark = pair.Key;
                    break;
                }
            }
            //currentBookMark = this.axControl.bookMarkCombo.Items[this.axControl.bookMarkCombo.SelectedIndex].ToString();
            if (this.axControl.bookMarkCombo.Items.Contains(currentBookMark))

                if (InputBox.Show("Remove your book", "Bookmark:", ref currentBookMark, validation) == DialogResult.OK)
                {
                    //MessageBox.Show(value);
                    //String text = this.SelectedText;

                    this.Select(start, length);
                    //this.SelectedText = oriText;
                    this.SelectionBackColor = Color.White;
                    bookMarkDict.Remove(currentBookMark);
                    this.axControl.bookMarkCombo.Items.Remove(currentBookMark);
                    persitBookMark();
                }


        }
        //Code to add right click

        public void FindBookMark(String bookMark)
        {
            if (bookMark.Equals("Bookmarks")) return;
            try
            {
                int start = Int32.Parse(bookMarkDict[bookMark][0]);
                int length = Int32.Parse(bookMarkDict[bookMark][1]);
                this.Select(start, length);
                // this.SelectionBackColor = Color.Chocolate;
                this.ScrollToCaret();
            }
            catch (Exception e) { }
        }

        void mi_Cut(object sender, EventArgs e)
        {
            this.Cut();
        }
        void mi_Copy(object sender, EventArgs e)
        {
            //Graphics objGraphics;
            Clipboard.SetData(DataFormats.Rtf, this.SelectedRtf);
            Clipboard.Clear();
        }

        void mi_Paste(object sender, EventArgs e)
        {
            if (Clipboard.ContainsText(TextDataFormat.Rtf))
            {
                this.SelectedRtf = Clipboard.GetData(DataFormats.Rtf).ToString();
            }
        }

        void mi_About(object sender, EventArgs e)
        {
            new AboutBox().Show();


        }


       
        #region "Avoid flickering"


        private const int WM_SETREDRAW = 0xB;

        [System.Runtime.InteropServices.DllImport("User32")]

        private static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

        private void FreezeDraw()
        { //Disable drawing

            SendMessage(this.Handle, WM_SETREDRAW, 0, 0);

        }

        private void UnfreezeDraw()
        { //Enable drawing and do a redraw. 

            SendMessage(this.Handle, WM_SETREDRAW, 1, 0);

            this.Invalidate(true);

        }

        #endregion

        public static String Wrap(string text, int maxLength)
        {

            // Return empty list of strings if the text was empty
            if (text.Length == 0) return "";

            var words = text.Split(' ');
            StringBuilder lines = new StringBuilder();
            var currentLine = "";

            foreach (var currentWord in words)
            {

                if ((currentLine.Length > maxLength) ||
                    ((currentLine.Length + currentWord.Length) > maxLength))
                {
                    lines.Append(currentLine +"\n");
                    currentLine = "";
                }

                if (currentLine.Length > 0)
                    currentLine += " " + currentWord;
                else
                    currentLine += currentWord;

            }

            if (currentLine.Length > 0)
                lines.Append(currentLine + "\n");


            return lines.ToString();
        }


   
    }


    
   



}

